package com.shimao.celnet.payment.service.apiservice;

import com.baiwang.bop.client.IBopClient;
import com.baiwang.bop.request.impl.invoice.common.InvoiceDetails;
import com.baiwang.bop.request.impl.invoice.common.InvoiceMainInfo;
import com.baiwang.bop.request.impl.invoice.impl.InvoiceOpenRequest;
import com.baiwang.bop.respose.entity.InvoiceOpenResponse;
import com.baiwang.bop.respose.entity.InvoiceQueryResponse;
import com.shimao.celnet.payment.common.exception.BaseException;
import com.shimao.celnet.payment.controller.apinotcheck.CallOut_invPdfOutController;
import com.shimao.celnet.payment.controller.apinotcheck.CallOut_invoiceQueryOutController;
import com.shimao.celnet.payment.model.PInvoiceBlue;
import com.shimao.celnet.payment.model.PInvoiceBlueDetail;
import com.shimao.celnet.payment.service.PInvoiceBlueDetailService;
import com.shimao.celnet.payment.service.PInvoiceBlueService;
import com.shimao.celnet.payment.service.SCodeNameService;
import com.shimao.celnet.payment.service.apiservice.CallOut_invOpenOutService;
import com.shimao.celnet.payment.service.impl.PInvoiceBlueDetailServiceImpl;
import com.shimao.celnet.payment.util.*;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;


import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Stream;

/**
 * 接口封装拆票Service
 * Created by galen on 2018/12/3.
 */
@Service
public class CallIn_outInvOpenLimitBlueService {

    protected Logger logger = Logger.getLogger(this.getClass());
    @Autowired
    private CallOut_invoiceQueryOutController callOut_invoiceQueryOutController;

    @Autowired
    private PInvoiceBlueDetailService pInvoiceBlueDetailService;

    @Autowired
    private PInvoiceBlueService pInvoiceBlueService;

    @Autowired
    private SCodeNameService sCodeNameService;

    @Autowired
    private CallOut_invOpenOutService invOpenOutService;

    @Autowired
    private CallOut_invPdfOutController callOut_invPdfOutController;


    //存放未超额的已经排列好的分组
    List<Map<String,Long>> groupList = new ArrayList<>();

    //存放超额的【未】组合好的分组
    List<Map<String,Long>> groupbeyondList = new ArrayList<>();


    //源数据
    Map<String,Long> exsysIdToPriceMap = new HashMap<String,Long>();
    //存放单张拆完剩余的Map《外部明细id,金额》
    Map<String,Long> exId2additionPriceMap = new LinkedHashMap<>();
    //存放拆完的并且总额和达到超额的
    StringBuffer guidlists= new StringBuffer();
    /**
     * 外部系统开票申请
     * @param requestJson
     * @throws BaseException
     */
    public JSONObject outInvOpenLimitBuleReqInService(PInvoiceBlue requestJson) throws IOException, ParseException, BaseException {
        JSONObject RetJsonObject = new JSONObject(); //返回参数
        String invoiceBlueParentGuid = requestJson.getGuid(); //源发票记录的guid
        String sellerTaxNo = requestJson.getSellerTaxNo();//销方税号，查询条件
        int DEFAULT_MONEY = 100; //用于开单张明细超额的默认阈值，以分为单位，100  = 1元
        //遍历开票明细，然后使用MapTotal<明细id，明细>装好
        Map<String,PInvoiceBlueDetail> detailReqInMap = new LinkedHashMap <>();
        Map<String,String> detailParentGuidMap = new LinkedHashMap <>();//源数据的明细的guid
        //查出所有的明细数据
        List<PInvoiceBlueDetail> pinvoiceDetailList = pInvoiceBlueDetailService.getByInvoiceGuid(requestJson.getGuid());
        System.out.println(pinvoiceDetailList.size());
        for (PInvoiceBlueDetail cobr :pinvoiceDetailList ){
            detailReqInMap.put(cobr.getExsysDetailId(),cobr);
            detailParentGuidMap.put(cobr.getExsysDetailId(),cobr.getGuid());
            exsysIdToPriceMap.put(cobr.getExsysDetailId(), Long.valueOf(cobr.getGoodsTotalPrice()));
        }
        //根据限额，把拼凑好的明细放到一个Map<明细id，明细>中，同时把MapTotal里面的数据删除，每装好一个放到List中。
        exsysIdToPriceMap = this.sortByValue(exsysIdToPriceMap);
        long standardPrice = Long.valueOf(requestJson.getLimitAccount()) - DEFAULT_MONEY; //标准开票金额=总金额 - 100（分）
        //循环去遍历，直到exsysIdToPriceMap没有记录 --》进行拆票，把大于限额的放到groupbeyondList 小于的放到 groupList
        while (exsysIdToPriceMap.size() > 0){
            this.groupByK(Long.valueOf(requestJson.getLimitAccount()));
        }
        //--------------单明细---------------------------------
        //单明细，超额  明细只有一条 && 明细金额大于上线金额
        boolean detailSize = pinvoiceDetailList.size() == 1;
        boolean isBeyondLimitation = detailSize?Long.valueOf(pinvoiceDetailList.get(0).getGoodsTotalPrice()) > Long.valueOf(requestJson.getLimitAccount()):false;
        if (detailSize && isBeyondLimitation){
            /**
             * 实际业务场景有，一张单明细开出十几张票的极端情况，所以也要考虑进去。
             * 每次拆出一条开票数据，都要生成一条新的开票记录，还有开票明细，同时他们都关联自己的源数据。
             * 插入数据库 TODO 需要到数据库的开票表和开票明细表添加一个字段叫parentGuid,
             * 同时新生成发票记录和发票明细插入数据库
             */
            Map<String ,Long> spiltResultMap =this.groupSingleByLimition(groupbeyondList,standardPrice);
            //遍历spiltResultMap，生成新的开票明细和开票记录并且关联源数据
            List<PInvoiceBlue> pInvoiceBlues = new ArrayList<>();
            List<PInvoiceBlueDetail> pInvoiceBlueDetails = new ArrayList<>();
            for(Map.Entry entry :spiltResultMap.entrySet()){
                String key = (String.valueOf(entry.getKey()));
                key = key.substring(0,key.indexOf("+"));
                PInvoiceBlueDetail detail = detailReqInMap.get(key);
                String []keys = key.split(",");
                if (detail != null){
                    String newBlueGUID = pInvoiceBlueService.getGuid();
                    //生成新的开票记录
                    PInvoiceBlue pInvoiceBlue = requestJson;
                    pInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源开票记录
                    pInvoiceBlue.setGuid(newBlueGUID); //生成新的开票记录
                    pInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                    pInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(entry.getValue()));//开票价税合计
                    //TODO 看下外部请求支付平台的json还需要增加什么参数，你添加进去
                    pInvoiceBlueService.save(pInvoiceBlue);
                    pInvoiceBlues.add(pInvoiceBlueService.getByKey(newBlueGUID));
                    PInvoiceBlueDetail newDetail = detail;
                    String newDetailGUID = pInvoiceBlueDetailService.getGuid();
                    newDetail.setParentGuid(detailParentGuidMap.get(key));//关联源开票明细
                    newDetail.setGuid(newDetailGUID);//生成新的开票明细
                    newDetail.setInvoiceGuid(newBlueGUID);//绑定新的开票记录,，便于后面扩展作废和红冲，打印
                    newDetail.setGoodsPrice(String.valueOf(entry.getValue()));
                    newDetail.setGoodsTotalPrice(String.valueOf(entry.getValue()));//记录开票的金额 TODO 这里需要注意，外部开票的价税合计，我们是存放在goodsTotalPrice
                    pInvoiceBlueDetailService.save(newDetail);
                    pInvoiceBlueDetails.add(pInvoiceBlueDetailService.getByKey(newDetailGUID));

                }else if(keys.length>0){
                    String newBlueGUID = pInvoiceBlueService.getGuid();
                    //生成新的开票记录
                    PInvoiceBlue pInvoiceBlue = requestJson;
                    pInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源开票记录
                    pInvoiceBlue.setGuid(newBlueGUID); //生成新的开票记录
                    pInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                    pInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(entry.getValue()));//开票价税合计
                    //TODO 看下外部请求支付平台的json还需要增加什么参数，你添加进去
                    pInvoiceBlueService.save(pInvoiceBlue);
                    pInvoiceBlues.add(pInvoiceBlueService.getByKey(newBlueGUID));
                    for (String k:
                            keys) {
                        exId2additionPriceMap.get(k);
                        PInvoiceBlueDetail newDetail = detailReqInMap.get(k);
                        String newDetailGUID = pInvoiceBlueDetailService.getGuid();
                        newDetail.setParentGuid(detailParentGuidMap.get(key));//关联源开票明细
                        newDetail.setGuid(newDetailGUID);//生成新的开票明细
                        newDetail.setInvoiceGuid(newBlueGUID);//绑定新的开票记录,，便于后面扩展作废和红冲，打印
                        newDetail.setGoodsPrice(String.valueOf(exId2additionPriceMap.get(k)));
                        newDetail.setGoodsTotalPrice(String.valueOf(exId2additionPriceMap.get(k)));//记录开票的金额 TODO 这里需要注意，外部开票的价税合计，我们是存放在goodsTotalPrice
                        pInvoiceBlueDetailService.save(newDetail);
                        pInvoiceBlueDetails.add(pInvoiceBlueDetailService.getByKey(newDetailGUID));
                    }
                }
            }
            //  pInvoiceBlueService.saveList(pInvoiceBlues);
            //    pInvoiceBlueDetailService.saveList(pInvoiceBlueDetails);
            //调用开票，开票成功更新开票数据状态，同时更新父开票的开票状态
            Map<List<PInvoiceBlueDetail>,InvoiceOpenResponse> invoiceOpenSingleResponseMap = new LinkedHashMap<>();
            for (PInvoiceBlue pib :pInvoiceBlues) {
                System.out.println("发票guid："+pib.getGuid());
                InvoiceOpenRequest request = new InvoiceOpenRequest();
                request.setSellerTaxNo(pib.getSellerTaxNo());//销方单位税号
                request.setDeviceType(pib.getDeviceType());//设备类型 0税控服务器，1税控盘
                request.setOrganizationCode(pib.getOrganizationCode());
                request.setSerialNo(pib.getSerialNo());//开票流水号，唯一标志开票请求。支持数字字母下划线组合。
                request.setInvoiceSpecialMark(pib.getInvoiceSpecialMark());//特殊票种标记，00：普通发票 02：农业发票 默认：00
                request.setInvoiceTypeCode(pib.getInvoiceTypeCode());//发票种类编码，004:增值税专用发票，007:增值税普通发票，026：增值税电子发票，025：增值税卷式发票
                request.setInvoiceTerminalCode(pib.getInvoiceYerminalCode());//开票点编码
                request.setBuyerTaxNo(pib.getBuyerTaxNo() == null ? "" : pib.getBuyerTaxNo());//购方单位税号,从前台页面传过来的map中获取
                request.setBuyerName(pib.getBuyerName());//购方单位名称,
                request.setBuyerAddressPhone(pib.getBuyerAddressPhone() == null ? "" : pib.getBuyerAddressPhone());//购方地址及电话专票必填
                request.setBuyerBankAccount(pib.getBuyerBankAccount() == null ? "" : pib.getBuyerBankAccount());//购方开户行及账号专票必填
                request.setDrawer(pib.getDrawer());//开票人
                request.setChecker(pib.getChecker());//复核人
                request.setPayee(pib.getPayee());//收款人
                request.setInvoiceType(FinalUtil.INVOICE_TYPE_BULE);//开票类型0:正数发票（蓝票） 1：负数发票（红票）
                request.setInvoiceListMark(pib.getInvoiceListMark());//“清单标志： 0：无清单 1：带清单 （发票明细大于等于8行必须带清单）”
                request.setTaxationMode(pib.getTaxationMode());//征税方式 0：普通征税，2：差额征税
                request.setDeductibleAmount(pib.getDeductibleAmount());//扣除额保留两位小数
                request.setInvoiceTotalPriceTax(MyDateUtil.getTxFloat(pib.getInvoiceTotalPriceTax()));//价税合计，保留两位小数
                System.out.println("发票金额："+MyDateUtil.getTxFloat(pib.getInvoiceTotalPriceTax()));
                request.setSignatureParameter(sCodeNameService.getBWListByType(FinalUtil.CODE_NAME_INVOICE_SIGNATURE_BW).get(0).getCode());//百望所需要的默认签名值
                if (pib.getDeviceType() == "1") {
                    //设备类型为1税控盘，必填
                    request.setTaxDiskNo(pib.getTaxDiskNo());//税控盘编号
                    request.setTaxDiskKey(pib.getTaxDiskKey());//税控盘口令
                    request.setTaxDiskPassword(pib.getTaxDiskPassword());//税务数字证书密码
                }
                request.setGoodsCodeVersion(pib.getGoodsCodeVersion());//商品编码版本号
                request.setConsolidatedTaxRate(pib.getConsolidatedTaxRate());//综合税率
                request.setNotificationNo(pib.getNotificationNo());//通知单编号
                request.setRemarks(pib.getHouseName() == null ? "" : pib.getHouseName() + "/" + pib.getRemarks() == null ? "" : pib.getRemarks());//备注
                //根据InvoiceGuid查询开票明细数据
                // TODO 这个需要重新写一个sql,把getByInvoiceGuid里面的判断条件加上（使用or）parentguid
                List<PInvoiceBlueDetail> pinvoiceDetailNewList = pInvoiceBlueDetailService.getByInvoiceGuid(pib.getGuid());
                List<InvoiceDetails> invoiceDetailsList = new ArrayList<InvoiceDetails>();
                for (PInvoiceBlueDetail pInvoiceDetail : pinvoiceDetailNewList) {
                    //百望开票明细
                    InvoiceDetails invoiceDetails = new InvoiceDetails();
                    invoiceDetails.setGoodsLineNo(pInvoiceDetail.getGoodsLineNo());//明细行号
                    invoiceDetails.setGoodsLineNature(pInvoiceDetail.getGoodsLineNature());//发票行性质，0：正常行 1：折扣行 2：被折扣行
                    invoiceDetails.setGoodsCode(pInvoiceDetail.getGoodsCode());//国税商品编码
                    invoiceDetails.setGoodsExtendCode(pInvoiceDetail.getGoodsExtendCode());//自定义商品编码
                    invoiceDetails.setGoodsName(pInvoiceDetail.getGoodsName());//自定义商品名称
                    invoiceDetails.setGoodsTaxItem(pInvoiceDetail.getGoodsTaxItem());//国税商品名称
                    invoiceDetails.setGoodsSpecification(pInvoiceDetail.getGoodsSpecification());
                    invoiceDetails.setGoodsUnit(pInvoiceDetail.getGoodsUnit());
                    invoiceDetails.setGoodsQuantity(pInvoiceDetail.getGoodsQuantity());
                    if (!("".equals(pInvoiceDetail.getGoodsPrice()) || pInvoiceDetail.getGoodsPrice() == null)) {
                        invoiceDetails.setGoodsPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsPrice()));
                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalPrice()) || pInvoiceDetail.getGoodsTotalPrice() == null)) {
                        invoiceDetails.setGoodsTotalPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalPrice()));//金额,单位：元，保留两位小数

                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalTax()) || pInvoiceDetail.getGoodsTotalTax() == null)) {
                        invoiceDetails.setGoodsTotalTax(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalTax()));//税额
                    }
                    invoiceDetails.setGoodsTaxRate(pInvoiceDetail.getGoodsTaxRate());//税率
                    invoiceDetails.setGoodsDiscountLineNo(pInvoiceDetail.getGoodsDiscountLineNo());
                    invoiceDetails.setPriceTaxMark(pInvoiceDetail.getPriceTaxMark());//含税标志0：不含税 1：含税
                    invoiceDetails.setVatSpecialManagement(pInvoiceDetail.getVatSpecialManagement());
                    invoiceDetails.setFreeTaxMark(pInvoiceDetail.getFreeTaxMark());
                    invoiceDetails.setPreferentialMark(pInvoiceDetail.getPreferential());//是否使用优惠政策 0:未使用，1:使用
                    invoiceDetailsList.add(invoiceDetails);
                }
                request.setInvoiceDetailsList(invoiceDetailsList);
                InvoiceOpenResponse response;
                try {
                    //调用开票
                    response = publicOpenInvoice(request);
                    invoiceOpenSingleResponseMap.put(pinvoiceDetailNewList, response);//<明细list，返回结果>
                    //开票成功后保存蓝票数据
                    if (response != null) {
                        //开票成功后，更新开票记录和明细上带上发票号
                        pib.setInvoiceCode(response.getInvoiceCode());//发票代码
                        pib.setInvoiceNo(response.getInvoiceNo());//发票号码
                        pib.setInvoiceEndTime(String.valueOf(MyDateUtil.StrToDate(response.getInvoiceDate(), MyDateUtil.format_yyyyMMddHHmmss).getTime()));//开票完成日期
                        pib.setTaxControlCode(response.getTaxControlCode());//税控码 对应发票的密码区
                        pib.setInvoiceCheckCode(response.getInvoiceCheckCode());//校验码
                        pib.setInvoiceQrcode(response.getInvoiceQrCode());//二维码
                        pib.setInvoiceStatus(FinalUtil.INVOICE_SUCCESS);//更新发票状态;开票成功
                        pInvoiceBlueService.update(pib); //更新发票记录
                        System.out.println("blue"+pib.getGuid()+"发票号："+pib.getInvoiceNo());
                        for (PInvoiceBlueDetail pb : pinvoiceDetailNewList){
                            pb.setInvoiceNo(response.getInvoiceNo());
                            //TODO 这里可以优化，就是自己写一个updateList的sql。这样把压力放到数据库，减少连接数
                            System.out.println("Detail"+pb.getGuid()+"发票号："+pb.getInvoiceNo());
                            pInvoiceBlueDetailService.update(pb);
                        }
                    }
                } catch (Exception e) {
                    throw new MyException(e.getMessage());
                }
            }

            /**
             *  把开出的所有发票号，取到源发票，然后把所有的发票的税额汇总，然后统一返回给请求方
             */
            JSONObject jsonObject = new JSONObject();
            JSONArray jsonArray = new JSONArray();
            //存放发票号汇总
            StringBuffer invoiceNos = new StringBuffer();
            float invoiceTotalPriceSum = 0.00f; // 总价税合计
            float InvoiceTotalTaxSum = 0.00f; //总税额
            float invoiceTotalPriceTaxSum = 0.00f; //总不含税金额
            /*float invoiceTotalPriceSum1 = 0.00f; // 总价税合计
            float InvoiceTotalTaxSum1 = 0.00f; //总税额
            float invoiceTotalPriceTaxSum1 = 0.00f; //总不含税金额*/
            for (Map.Entry entry : invoiceOpenSingleResponseMap.entrySet()) {
                //获取开票返回记录
                InvoiceOpenResponse response = (InvoiceOpenResponse)entry.getValue();
                //拼凑参数进行调用发票查询接口
                PInvoiceBlue pInvoiceBlue = new PInvoiceBlue();
                pInvoiceBlue.setInvoiceCode(response.getInvoiceCode());
                pInvoiceBlue.setGuid(pInvoiceBlueService.getGuidByInvoiceNo(response.getInvoiceNo()));
                pInvoiceBlue.setInvoiceNo(response.getInvoiceNo());
                // TODO 开始日期和截止日期都填了一样的，目前没影响，如果后期发现需要按日期查发票，那么这里就需要改
                pInvoiceBlue.setInvoiceStartTime(response.getInvoiceDate());
                pInvoiceBlue.setInvoiceEndTime(response.getInvoiceDate());
                pInvoiceBlue.setSellerTaxNo(sellerTaxNo);         //销方税号
                DecimalFormat fnum = new DecimalFormat("##0.00");
                InvoiceQueryResponse responseRet = callOut_invoiceQueryOutController.invoiceBlueQueryOut(pInvoiceBlue);
                for (InvoiceMainInfo invoiceMainInfo : responseRet.getInvoiceList()) {
                    jsonObject.put("receiptGuid", invoiceBlueParentGuid);//记录guid（单位：分）
                    jsonObject.put("blueRedType", invoiceMainInfo.getInvoiceType());
                    jsonObject.put("invoiceTypeCode", requestJson.getInvoiceTypeCode());
                    jsonObject.put("invoiceCode", invoiceMainInfo.getInvoiceCode());//发票代码（单位：分）
                    // jsonObject.put("invoiceNo", invoiceMainInfo.getInvoiceNo());//发票号码
                    invoiceNos.append(invoiceMainInfo.getInvoiceNo()+",");
                    jsonObject.put("invoiceDate", invoiceMainInfo.getInvoiceDate());//开票日期
                    jsonObject.put("taxControlCode", invoiceMainInfo.getTaxationMode());//税控码
                    jsonObject.put("invoiceCheckCode", invoiceMainInfo.getInvoiceCheckCode());//校验码
                    jsonObject.put("invoiceQrCode", invoiceMainInfo.getInvoiceQrCode());//二维码
                    jsonObject.put("deductibleAmount", invoiceMainInfo.getDeductibleAmount());//扣除额
                    jsonObject.put("invoiceTotalPrice", invoiceMainInfo.getInvoiceTotalPrice());//合计金额
                    jsonObject.put("invoiceTotalTax", invoiceMainInfo.getInvoiceTotalTax());//合计税额
                    jsonObject.put("invoiceTotalPriceTax", invoiceMainInfo.getInvoiceTotalPriceTax());//价税合计
                    /*invoiceTotalPriceSum1 = invoiceTotalPriceSum1+ Float.valueOf(invoiceMainInfo.getInvoiceTotalPrice());
                    InvoiceTotalTaxSum1 = InvoiceTotalTaxSum1 + Float.valueOf(invoiceMainInfo.getInvoiceTotalTax());
                    invoiceTotalPriceTaxSum1 = invoiceTotalPriceTaxSum1 + Float.valueOf(invoiceMainInfo.getInvoiceTotalPriceTax());*/
                    //如果是电子发票，则需要调用版式生成的接口
                    if (FinalUtil.INVOICE_TYPE_CODE_026.equals(invoiceMainInfo.getInvoiceTypeCode())) {
                        pInvoiceBlue.setSellerTaxNo(invoiceMainInfo.getSellerTaxNo());//纳税人识别号
                        pInvoiceBlue.setSerialNo(invoiceMainInfo.getSerialNo());//开票流水号
                        pInvoiceBlue.setBuyerPhone(invoiceMainInfo.getBuyerPhone());// 购方客户电话
                        pInvoiceBlue.setBuyerEmail(invoiceMainInfo.getBuyerEmail());// 购方客户邮箱
                        jsonObject.put("invoiceUrl", callOut_invPdfOutController.invPdfOut(pInvoiceBlue));//版式生成电子url
                    }
                    for (PInvoiceBlueDetail invoiceDetails : (List<PInvoiceBlueDetail>) entry.getKey()) {
                        JSONObject json = new JSONObject();
                        //价税合计
                        float totalPriceTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice());
                        //税额 = 价税合计 * 税率 / （1+税率）
                        float totalTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice()) * Float.valueOf(invoiceDetails.getGoodsTaxRate()) / (1 + Float.valueOf(invoiceDetails.getGoodsTaxRate()));

                        //金额（不含税）
                        float totalPrice = (totalPriceTax*100 - totalTax*100)/100;
                        //TODO [注意] invoiceGuid作用很大，后面作废，红冲叫他们都使用这个guid
                        json.put("invoiceGuid",invoiceDetails.getInvoiceGuid()); //把支付平台拆分生成的guid回调回去。
                        json.put("exsysDetailID", invoiceDetails.getExsysDetailId());//外部系统明细idjson.put("goodsLineNo", InvoiceDetailsList.getGoodsLineNo());//明细行号
                        json.put("invoiceNo", invoiceDetails.getInvoiceNo());//拆分后明细对应发票号
                        //TODO 这里存在小数点精确不够的风险，目前没暴露出来。后期如果发现，可能就是这里的问题
                        json.put("goodsTotalPriceTax", fnum.format(totalPriceTax / 100));//价税金额（单位：分），开票时传入的金额
                        json.put("goodsTotalPrice", fnum.format(totalPrice / 100));//税金额（不含税）=价税金额 - 税额（单位：元）
                        json.put("goodsTotalTax", fnum.format(totalTax / 100));//税额（单位：元）
                        json.put("goodsTaxRate", invoiceDetails.getGoodsTaxRate());//税率
                        jsonArray.add(json);
                        invoiceTotalPriceSum = invoiceTotalPriceSum + totalPriceTax;
                        InvoiceTotalTaxSum = InvoiceTotalTaxSum + totalTax;
                        invoiceTotalPriceTaxSum = invoiceTotalPriceTaxSum + totalPrice;
                    }

                    if(invoiceNos.length() > 0){ //截取最后一个逗号
                        jsonObject.put("invoiceNo", invoiceNos.substring(0,invoiceNos.length()-1));//发票号汇总
                    }
                    jsonObject.put("invoiceTotalPrice", fnum.format(invoiceTotalPriceSum / 100));
                    jsonObject.put("invoiceTotalTax", fnum.format(InvoiceTotalTaxSum / 100));
                    jsonObject.put("invoiceTotalPriceTax", fnum.format(invoiceTotalPriceTaxSum / 100));
                    jsonObject.put("invoiceDetailsList", jsonArray);

                }
            }
            System.out.println("返回结果"+jsonObject.toString());
            RetJsonObject = jsonObject;
        }


        //---------------多明细----------------------------------------
        boolean mutipleDetailSize = pinvoiceDetailList.size() > 1;
        boolean isNoBeyondLimitation = true; //没有明细金额超过上限金额
        for (PInvoiceBlueDetail cobr :pinvoiceDetailList) {
            if (Long.valueOf(cobr.getGoodsTotalPrice()) > Long.valueOf(requestJson.getLimitAccount())){
                isNoBeyondLimitation = false;//明细中包含金额超过上限金额的数据
                break;
            }
        }
        //没有单条明细超额的情况。这种业务场景只需要考虑把发票拼凑，拼好了直接开票
        List<PInvoiceBlue> pInvoiceBlues = new ArrayList<>();
        String invoiceGuid;
        if (mutipleDetailSize && isNoBeyondLimitation){
            //invoiceGuid = pInvoiceBlueService.getGuid();
            //遍历groupList，根据明细生成一条开票数据，同时开票数据关联源数据
            for (Map<String,Long> map:groupList) {
                invoiceGuid = pInvoiceBlueService.getGuid();
                long TotalPriceTax = 0;
                long TotalPrice = 0;
                long TotalTax = 0;
                for (Map.Entry entry: map.entrySet()){
                    PInvoiceBlueDetail detail = detailReqInMap.get(entry.getKey());
                    TotalPriceTax += Long.valueOf(detail.getGoodsTotalPrice());
                    TotalPrice += Long.valueOf(detail.getGoodsPrice());
                    TotalTax += detail.getGoodsTotalTax() == null?0:Long.valueOf(detail.getGoodsTotalTax());
                    detail.setNewInvoiceGuid(invoiceGuid); //让明细关联上新的开票信息guid
                    pInvoiceBlueDetailService.update(detail);
                }
                PInvoiceBlue newPInvoiceBlue = requestJson;
                newPInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(TotalPriceTax)); //价税合计
                newPInvoiceBlue.setInvoiceTotalPrice(String.valueOf(TotalPrice));//发票金额
                newPInvoiceBlue.setInvoiceTotalTax(String.valueOf(TotalTax)); //发票税额
                newPInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                newPInvoiceBlue.setGuid(invoiceGuid); //生成GUID
                newPInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源GUID
                pInvoiceBlueService.save(newPInvoiceBlue);
                pInvoiceBlues.add(pInvoiceBlueService.getByKey(invoiceGuid));
            }
            // pInvoiceBlueService.saveList(pInvoiceBlues);//生成新的发票信息

            //请求开票
            Map<List<PInvoiceBlueDetail>,InvoiceOpenResponse> invoiceOpenResponseMap = new LinkedHashMap<>();
            for (PInvoiceBlue pib:pInvoiceBlues) {
                InvoiceOpenRequest request = new InvoiceOpenRequest();
                request.setSellerTaxNo(pib.getSellerTaxNo());//销方单位税号
                request.setDeviceType(pib.getDeviceType());//设备类型 0税控服务器，1税控盘
                request.setOrganizationCode(pib.getOrganizationCode());
                request.setSerialNo(pib.getSerialNo());//开票流水号，唯一标志开票请求。支持数字字母下划线组合。
                request.setInvoiceSpecialMark(pib.getInvoiceSpecialMark());//特殊票种标记，00：普通发票 02：农业发票 默认：00
                request.setInvoiceTypeCode(pib.getInvoiceTypeCode());//发票种类编码，004:增值税专用发票，007:增值税普通发票，026：增值税电子发票，025：增值税卷式发票
                request.setInvoiceTerminalCode(pib.getInvoiceYerminalCode());//开票点编码
                request.setBuyerTaxNo(pib.getBuyerTaxNo() == null ? "" : pib.getBuyerTaxNo());//购方单位税号,从前台页面传过来的map中获取
                request.setBuyerName(pib.getBuyerName());//购方单位名称,
                request.setBuyerAddressPhone(pib.getBuyerAddressPhone() == null ? "" : pib.getBuyerAddressPhone());//购方地址及电话专票必填
                request.setBuyerBankAccount(pib.getBuyerBankAccount() == null ? "" : pib.getBuyerBankAccount());//购方开户行及账号专票必填
                request.setDrawer(pib.getDrawer());//开票人
                request.setChecker(pib.getChecker());//复核人
                request.setPayee(pib.getPayee());//收款人
                request.setInvoiceType(FinalUtil.INVOICE_TYPE_BULE);//开票类型0:正数发票（蓝票） 1：负数发票（红票）
                request.setInvoiceListMark(pib.getInvoiceListMark());//“清单标志： 0：无清单 1：带清单 （发票明细大于等于8行必须带清单）”
                request.setTaxationMode(pib.getTaxationMode());//征税方式 0：普通征税，2：差额征税
                request.setDeductibleAmount(pib.getDeductibleAmount());//扣除额保留两位小数
                request.setInvoiceTotalPriceTax(MyDateUtil.getTxFloat(pib.getInvoiceTotalPriceTax()));//价税合计，保留两位小数
                request.setSignatureParameter(sCodeNameService.getBWListByType(FinalUtil.CODE_NAME_INVOICE_SIGNATURE_BW).get(0).getCode());//百望所需要的默认签名值
                if (pib.getDeviceType() == "1") {
                    //设备类型为1税控盘，必填
                    request.setTaxDiskNo(pib.getTaxDiskNo());//税控盘编号
                    request.setTaxDiskKey(pib.getTaxDiskKey());//税控盘口令
                    request.setTaxDiskPassword(pib.getTaxDiskPassword());//税务数字证书密码
                }
                request.setGoodsCodeVersion(pib.getGoodsCodeVersion());//商品编码版本号
                request.setConsolidatedTaxRate(pib.getConsolidatedTaxRate());//综合税率
                request.setNotificationNo(pib.getNotificationNo());//通知单编号
                request.setRemarks(pib.getHouseName() == null ? "" : pib.getHouseName() + "/" + pib.getRemarks() == null ? "" : pib.getRemarks());//备注
                //根据InvoiceGuid查询开票明细数据
                // TODO 这个需要重新写一个sql,把getByInvoiceGuid里面的判断条件加上（使用or）parentguid
                List<PInvoiceBlueDetail> pinvoiceDetailNewList = pInvoiceBlueDetailService.getByInvoiceGuid(pib.getGuid());
                System.out.println("发票guid："+pib.getGuid()+" 明细数量："+pinvoiceDetailNewList.size());
                List<InvoiceDetails> invoiceDetailsList = new ArrayList<InvoiceDetails>();
                for (PInvoiceBlueDetail pInvoiceDetail : pinvoiceDetailNewList) {
                    //百望开票明细
                    InvoiceDetails invoiceDetails = new InvoiceDetails();
                    invoiceDetails.setGoodsLineNo(pInvoiceDetail.getGoodsLineNo());//明细行号
                    invoiceDetails.setGoodsLineNature(pInvoiceDetail.getGoodsLineNature());//发票行性质，0：正常行 1：折扣行 2：被折扣行
                    invoiceDetails.setGoodsCode(pInvoiceDetail.getGoodsCode());//国税商品编码
                    invoiceDetails.setGoodsExtendCode(pInvoiceDetail.getGoodsExtendCode());//自定义商品编码
                    invoiceDetails.setGoodsName(pInvoiceDetail.getGoodsName());//自定义商品名称
                    invoiceDetails.setGoodsTaxItem(pInvoiceDetail.getGoodsTaxItem());//国税商品名称
                    invoiceDetails.setGoodsSpecification(pInvoiceDetail.getGoodsSpecification());
                    invoiceDetails.setGoodsUnit(pInvoiceDetail.getGoodsUnit());
                    invoiceDetails.setGoodsQuantity(pInvoiceDetail.getGoodsQuantity());
                    if (!("".equals(pInvoiceDetail.getGoodsPrice()) || pInvoiceDetail.getGoodsPrice() == null)) {
                        invoiceDetails.setGoodsPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsPrice()));
                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalPrice()) || pInvoiceDetail.getGoodsTotalPrice() == null)) {
                        invoiceDetails.setGoodsTotalPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalPrice()));//金额,单位：元，保留两位小数

                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalTax()) || pInvoiceDetail.getGoodsTotalTax() == null)) {
                        invoiceDetails.setGoodsTotalTax(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalTax()));//税额
                    }
                    invoiceDetails.setGoodsTaxRate(pInvoiceDetail.getGoodsTaxRate());//税率
                    invoiceDetails.setGoodsDiscountLineNo(pInvoiceDetail.getGoodsDiscountLineNo());
                    invoiceDetails.setPriceTaxMark(pInvoiceDetail.getPriceTaxMark());//含税标志0：不含税 1：含税
                    invoiceDetails.setVatSpecialManagement(pInvoiceDetail.getVatSpecialManagement());
                    invoiceDetails.setFreeTaxMark(pInvoiceDetail.getFreeTaxMark());
                    invoiceDetails.setPreferentialMark(pInvoiceDetail.getPreferential());//是否使用优惠政策 0:未使用，1:使用
                    invoiceDetailsList.add(invoiceDetails);
                }
                request.setInvoiceDetailsList(invoiceDetailsList);
                InvoiceOpenResponse response;
                try {
                    //调用开票
                    response = publicOpenInvoice(request);
                    invoiceOpenResponseMap.put(pinvoiceDetailNewList, response);//<明细list，返回结果>
                    //开票成功后保存蓝票数据
                    if (response != null) {
                        //开票成功后，更新开票记录和明细上带上发票号
                        pib.setInvoiceCode(response.getInvoiceCode());//发票代码
                        pib.setInvoiceNo(response.getInvoiceNo());//发票号码
                        pib.setInvoiceEndTime(String.valueOf(MyDateUtil.StrToDate(response.getInvoiceDate(), MyDateUtil.format_yyyyMMddHHmmss).getTime()));//开票完成日期
                        pib.setTaxControlCode(response.getTaxControlCode());//税控码 对应发票的密码区
                        pib.setInvoiceCheckCode(response.getInvoiceCheckCode());//校验码
                        pib.setInvoiceQrcode(response.getInvoiceQrCode());//二维码
                        pib.setInvoiceStatus(FinalUtil.INVOICE_SUCCESS);//更新发票状态;开票成功
                        pInvoiceBlueService.update(pib); //更新发票记录
                        for (PInvoiceBlueDetail pb : pinvoiceDetailNewList){
                            pb.setInvoiceNo(response.getInvoiceNo());
                            //TODO 这里可以优化，就是自己写一个updateList的sql。这样把压力放到数据库，减少连接数
                            pInvoiceBlueDetailService.update(pb);
                        }
                    }
                } catch (Exception e) {
                    throw new MyException(e.getMessage());
                }
            }
            /**
             * 开票成功后，根据外部明细id，在对应的明细上带上发票号
             * 1，把发票号更新到明细上的，开票记录上
             * 2，回调开票成功数据。拼接参数的时候需要注意：
             *      1），需要把所有的发票号汇总到开票记录上，用‘，’隔开
             *      2），每条明细上要加上对应的发票号
             *      3），每条明细里面加上新生成的parentGUid。这样便于后面的作废和红冲
             *      TODO 在红冲的时候需要更改 ,在查明细的时候需要加上根据明细上新加的字段parentGUID去查询（使用OR条件）
             *      TODO  pInvoiceBlueDetailService（144行）.getByInvoiceGuid改造这个方法就可以
             */
            //调用发票查询接口
            //  3、调用百望开票结果查询接口，查询开票结果

            JSONObject jsonObject = new JSONObject();
            JSONArray jsonArray = new JSONArray();
            //存放发票号汇总
            StringBuffer invoiceNos = new StringBuffer();
            float invoiceTotalPriceSum = 0.00f; // 总价税合计
            float InvoiceTotalTaxSum = 0.00f; //总税额
            float invoiceTotalPriceTaxSum = 0.00f; //总不含税金额
            for (Map.Entry entry : invoiceOpenResponseMap.entrySet()) {
                //获取开票返回记录
                InvoiceOpenResponse response = (InvoiceOpenResponse)entry.getValue();
                //拼凑参数进行调用发票查询接口
                PInvoiceBlue pInvoiceBlue = new PInvoiceBlue();
                pInvoiceBlue.setInvoiceCode(response.getInvoiceCode());
                pInvoiceBlue.setGuid(pInvoiceBlueService.getGuidByInvoiceNo(response.getInvoiceNo()));
                pInvoiceBlue.setInvoiceNo(response.getInvoiceNo());
                // TODO 开始日期和截止日期都填了一样的，目前没影响，如果后期发现需要按日期查发票，那么这里就需要改
                pInvoiceBlue.setInvoiceStartTime(response.getInvoiceDate());
                pInvoiceBlue.setInvoiceEndTime(response.getInvoiceDate());
                pInvoiceBlue.setSellerTaxNo(sellerTaxNo);         //销方税号
                DecimalFormat fnum = new DecimalFormat("##0.00");
                InvoiceQueryResponse responseRet = callOut_invoiceQueryOutController.invoiceBlueQueryOut(pInvoiceBlue);
                for (InvoiceMainInfo invoiceMainInfo : responseRet.getInvoiceList()) {
                    jsonObject.put("receiptGuid", invoiceBlueParentGuid);//记录guid（单位：分）
                    jsonObject.put("blueRedType", invoiceMainInfo.getInvoiceType());
                    jsonObject.put("invoiceTypeCode", requestJson.getInvoiceTypeCode());
                    jsonObject.put("invoiceCode", invoiceMainInfo.getInvoiceCode());//发票代码（单位：分）
                    //jsonObject.put("invoiceNo", invoiceMainInfo.getInvoiceNo());//发票号码
                    invoiceNos.append(invoiceMainInfo.getInvoiceNo()+",");
                    jsonObject.put("invoiceDate", invoiceMainInfo.getInvoiceDate());//开票日期
                    jsonObject.put("taxControlCode", invoiceMainInfo.getTaxationMode());//税控码
                    jsonObject.put("invoiceCheckCode", invoiceMainInfo.getInvoiceCheckCode());//校验码
                    jsonObject.put("invoiceQrCode", invoiceMainInfo.getInvoiceQrCode());//二维码
                    jsonObject.put("deductibleAmount", invoiceMainInfo.getDeductibleAmount());//扣除额
                    jsonObject.put("invoiceTotalPrice", invoiceMainInfo.getInvoiceTotalPrice());//合计金额
                    jsonObject.put("invoiceTotalTax", invoiceMainInfo.getInvoiceTotalTax());//合计税额
                    jsonObject.put("invoiceTotalPriceTax", invoiceMainInfo.getInvoiceTotalPriceTax());//价税合计
                    //如果是电子发票，则需要调用版式生成的接口
                    if (FinalUtil.INVOICE_TYPE_CODE_026.equals(invoiceMainInfo.getInvoiceTypeCode())) {
                        pInvoiceBlue.setSellerTaxNo(invoiceMainInfo.getSellerTaxNo());//纳税人识别号
                        pInvoiceBlue.setSerialNo(invoiceMainInfo.getSerialNo());//开票流水号
                        pInvoiceBlue.setBuyerPhone(invoiceMainInfo.getBuyerPhone());// 购方客户电话
                        pInvoiceBlue.setBuyerEmail(invoiceMainInfo.getBuyerEmail());// 购方客户邮箱
                        jsonObject.put("invoiceUrl", callOut_invPdfOutController.invPdfOut(pInvoiceBlue));//版式生成电子url
                    }
                    for (PInvoiceBlueDetail invoiceDetails : (List<PInvoiceBlueDetail>) entry.getKey()) {
                        JSONObject json = new JSONObject();
                        //价税合计
                        float totalPriceTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice());
                        //税额 = 价税合计 * 税率 / （1+税率）
                        float totalTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice()) * Float.valueOf(invoiceDetails.getGoodsTaxRate()) / (1 + Float.valueOf(invoiceDetails.getGoodsTaxRate()));
                        //金额（不含税）
                        float totalPrice = (totalPriceTax*100 - totalTax*100)/100;
                        //TODO [注意] 这个guid的作用很大，后面作废，红冲叫他们都使用这个guid
                        json.put("invoiceGuid",invoiceDetails.getInvoiceGuid()); //把支付平台拆分生成的guid回调回去。
                        json.put("exsysDetailID", invoiceDetails.getExsysDetailId());//外部系统明细id
                        json.put("goodsLineNo", invoiceDetails.getGoodsLineNo());//明细行号
                        json.put("invoiceNo", invoiceDetails.getInvoiceNo());//拆分后明细对应发票号
                        //TODO 这里存在小数点精确不够的风险，目前没暴露出来。后期如果发现，可能就是这里的问题
                        json.put("goodsTotalPriceTax", fnum.format(totalPriceTax / 100));//价税金额（单位：分），开票时传入的金额
                        json.put("goodsTotalPrice", fnum.format(totalPrice / 100));//税金额（不含税）=价税金额 - 税额（单位：元）
                        json.put("goodsTotalTax", fnum.format(totalTax / 100));//税额（单位：元）
                        json.put("goodsTaxRate", invoiceDetails.getGoodsTaxRate());//税率
                        jsonArray.add(json);
                        invoiceTotalPriceSum = invoiceTotalPriceSum + totalPriceTax;
                        InvoiceTotalTaxSum = InvoiceTotalTaxSum + totalTax;
                        invoiceTotalPriceTaxSum = invoiceTotalPriceTaxSum + totalPrice;
                    }
                    if(invoiceNos.length() > 0){ //截取最后一个逗号
                        jsonObject.put("invoiceNo", invoiceNos.substring(0,invoiceNos.length()-1));//发票号汇总
                    }
                    jsonObject.put("invoiceTotalPrice", fnum.format(invoiceTotalPriceSum / 100));
                    jsonObject.put("invoiceTotalTax", fnum.format(InvoiceTotalTaxSum / 100));
                    jsonObject.put("invoiceTotalPriceTax", fnum.format(invoiceTotalPriceTaxSum / 100));
                    jsonObject.put("invoiceDetailsList", jsonArray);
                }

            }
            System.out.println("返回结果"+jsonObject.toString());
            RetJsonObject = jsonObject;
        }

        //有单条明细超额的情况。这种业务场景需要把未超额发票拼凑，超额的进行再拆分拼凑
        //超额拼凑规则：当有多笔超额的时候，把 每条超额的明细开到限额下的，然后进行开票，剩下的部分金额进行合并，形成新的开票金额
        if (mutipleDetailSize && !isNoBeyondLimitation){
            List<PInvoiceBlue> pInvoiceBlues1 = new ArrayList<>();
            List<PInvoiceBlueDetail> pInvoiceBlueDetails = new ArrayList<>();
            //遍历groupbeyondList，根据明细生成开票数据，同时开票数据要关联源数据。拆分规则:把 每条超额的明细开到限额下的，然后进行开票，剩下的部分金额进行合并，形成新的开票金额
            // for (Map<String,Long> groups:groupbeyondList) {}
            //拆分规则，先按照低于开票限额一元的方式把所有的都进行拆，然后最后把剩余得到进行组合
            /**
             * 实际业务场景有，一张单明细开出十几张票的极端情况，所以也要考虑进去。
             * 每次拆出一条开票数据，都要生成一条新的开票记录，还有开票明细，同时他们都关联自己的源数据。
             * 插入数据库 TODO 需要到数据库的开票表和开票明细表添加一个字段叫parentGuid,
             * 同时新生成发票记录和发票明细插入数据库
             */
            Map<String ,Long> spiltResultMap =this.groupSingleByLimition(groupbeyondList,standardPrice);
            //遍历spiltResultMap，生成新的开票明细和开票记录并且关联源数据

            for(Map.Entry entry :spiltResultMap.entrySet()){
                String key = (String.valueOf(entry.getKey()));
                key = key.substring(0,key.indexOf("+"));
                PInvoiceBlueDetail detail = detailReqInMap.get(key);
                String []keys = key.split(",");
                if (detail != null){
                    String newBlueGUID = pInvoiceBlueService.getGuid();
                    //生成新的开票记录
                    PInvoiceBlue pInvoiceBlue = requestJson;
                    pInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源开票记录
                    pInvoiceBlue.setGuid(newBlueGUID); //生成新的开票记录
                    pInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                    pInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(entry.getValue()));//开票价税合计
                    //TODO 看下外部请求支付平台的json还需要增加什么参数，你添加进去
                    pInvoiceBlueService.save(pInvoiceBlue);
                    pInvoiceBlues1.add(pInvoiceBlueService.getByKey(newBlueGUID));
                    PInvoiceBlueDetail newDetail = detail;
                    String newDetailGUID = pInvoiceBlueDetailService.getGuid();
                    newDetail.setParentGuid(detailParentGuidMap.get(key));//关联源开票明细
                    newDetail.setGuid(newDetailGUID);//生成新的开票明细
                    newDetail.setInvoiceGuid(newBlueGUID);//绑定新的开票记录,，便于后面扩展作废和红冲，打印
                    newDetail.setGoodsPrice(String.valueOf(entry.getValue()));
                    newDetail.setGoodsTotalPrice(String.valueOf(entry.getValue()));//记录开票的金额 TODO 这里需要注意，外部开票的价税合计，我们是存放在goodsTotalPrice
                    pInvoiceBlueDetailService.save(newDetail);
                    pInvoiceBlueDetails.add(pInvoiceBlueDetailService.getByKey(newDetailGUID));

                }else if(keys.length>0){
                    String newBlueGUID = pInvoiceBlueService.getGuid();
                    //生成新的开票记录
                    PInvoiceBlue pInvoiceBlue = requestJson;
                    pInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源开票记录
                    pInvoiceBlue.setGuid(newBlueGUID); //生成新的开票记录
                    pInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                    pInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(entry.getValue()));//开票价税合计
                    //TODO 看下外部请求支付平台的json还需要增加什么参数，你添加进去
                    pInvoiceBlueService.save(pInvoiceBlue);
                    pInvoiceBlues1.add(pInvoiceBlueService.getByKey(newBlueGUID));
                    for (String k:
                            keys) {
                        exId2additionPriceMap.get(k);  //TODO 这里待开发=======================================================
                        PInvoiceBlueDetail newDetail = detailReqInMap.get(k);
                        String newDetailGUID = pInvoiceBlueDetailService.getGuid();
                        newDetail.setParentGuid(detailParentGuidMap.get(key));//关联源开票明细
                        newDetail.setGuid(newDetailGUID);//生成新的开票明细
                        newDetail.setInvoiceGuid(newBlueGUID);//绑定新的开票记录,，便于后面扩展作废和红冲，打印
                        newDetail.setGoodsPrice(String.valueOf(exId2additionPriceMap.get(k)));
                        newDetail.setGoodsTotalPrice(String.valueOf(exId2additionPriceMap.get(k)));//记录开票的金额 TODO 这里需要注意，外部开票的价税合计，我们是存放在goodsTotalPrice
                        pInvoiceBlueDetailService.save(newDetail);
                        pInvoiceBlueDetails.add(pInvoiceBlueDetailService.getByKey(newDetailGUID));
                    }
                }
            }
            //  pInvoiceBlueService.saveList(pInvoiceBlues);
            //    pInvoiceBlueDetailService.saveList(pInvoiceBlueDetails);

            //遍历groupList，根据明细生成一条开票数据，同时开票数据关联源数据
            for (Map<String,Long> group:groupList){
                //结束循环的标志
                if(group.size() == 0){
                    continue;
                }
                //不需要拆分，用组合好的数据进行开票  每一条组合好的明细数据都要生成对应的一条开票信息
                //invoiceGuid = pInvoiceBlueService.getGuid();
                //遍历groupList，根据明细生成一条开票数据，同时开票数据关联源数据
                invoiceGuid = pInvoiceBlueService.getGuid();
                long TotalPriceTax = 0;
                long TotalPrice = 0;
                long TotalTax = 0;
                for (Map.Entry entry: group.entrySet()){
                    PInvoiceBlueDetail detail = detailReqInMap.get(entry.getKey());
                    TotalPriceTax += Long.valueOf(detail.getGoodsTotalPrice());
                    TotalPrice += Long.valueOf(detail.getGoodsPrice());
                    TotalTax += detail.getGoodsTotalTax() == null?0:Long.valueOf(detail.getGoodsTotalTax());
                    detail.setNewInvoiceGuid(invoiceGuid); //让明细关联上新的开票信息guid
                    pInvoiceBlueDetailService.update(detail);
                }

                PInvoiceBlue newPInvoiceBlue = requestJson;
                newPInvoiceBlue.setInvoiceTotalPriceTax(String.valueOf(TotalPriceTax)); //价税合计
                newPInvoiceBlue.setInvoiceTotalPrice(String.valueOf(TotalPrice));//发票金额
                newPInvoiceBlue.setInvoiceTotalTax(String.valueOf(TotalTax)); //发票税额
                newPInvoiceBlue.setSerialNo(sCodeNameService.getInvoiceSellerNumber());//生成新的流水号
                newPInvoiceBlue.setGuid(invoiceGuid); //生成GUID
                newPInvoiceBlue.setParentGuid(invoiceBlueParentGuid);//关联源GUID
                pInvoiceBlueService.save(newPInvoiceBlue);
                pInvoiceBlues1.add(pInvoiceBlueService.getByKey(invoiceGuid));


            }
            //请求开票
            //调用开票，开票成功更新开票数据状态，同时更新父开票的开票状态
            Map<List<PInvoiceBlueDetail>,InvoiceOpenResponse> invoiceOpenSingleResponseMap = new LinkedHashMap<>();
            for (PInvoiceBlue pib :pInvoiceBlues1) {
                System.out.println("发票guid："+pib.getGuid());
                InvoiceOpenRequest request = new InvoiceOpenRequest();
                request.setSellerTaxNo(pib.getSellerTaxNo());//销方单位税号
                request.setDeviceType(pib.getDeviceType());//设备类型 0税控服务器，1税控盘
                request.setOrganizationCode(pib.getOrganizationCode());
                request.setSerialNo(pib.getSerialNo());//开票流水号，唯一标志开票请求。支持数字字母下划线组合。
                request.setInvoiceSpecialMark(pib.getInvoiceSpecialMark());//特殊票种标记，00：普通发票 02：农业发票 默认：00
                request.setInvoiceTypeCode(pib.getInvoiceTypeCode());//发票种类编码，004:增值税专用发票，007:增值税普通发票，026：增值税电子发票，025：增值税卷式发票
                request.setInvoiceTerminalCode(pib.getInvoiceYerminalCode());//开票点编码
                request.setBuyerTaxNo(pib.getBuyerTaxNo() == null ? "" : pib.getBuyerTaxNo());//购方单位税号,从前台页面传过来的map中获取
                request.setBuyerName(pib.getBuyerName());//购方单位名称,
                request.setBuyerAddressPhone(pib.getBuyerAddressPhone() == null ? "" : pib.getBuyerAddressPhone());//购方地址及电话专票必填
                request.setBuyerBankAccount(pib.getBuyerBankAccount() == null ? "" : pib.getBuyerBankAccount());//购方开户行及账号专票必填
                request.setDrawer(pib.getDrawer());//开票人
                request.setChecker(pib.getChecker());//复核人
                request.setPayee(pib.getPayee());//收款人
                request.setInvoiceType(FinalUtil.INVOICE_TYPE_BULE);//开票类型0:正数发票（蓝票） 1：负数发票（红票）
                request.setInvoiceListMark(pib.getInvoiceListMark());//“清单标志： 0：无清单 1：带清单 （发票明细大于等于8行必须带清单）”
                request.setTaxationMode(pib.getTaxationMode());//征税方式 0：普通征税，2：差额征税
                request.setDeductibleAmount(pib.getDeductibleAmount());//扣除额保留两位小数
                request.setInvoiceTotalPriceTax(MyDateUtil.getTxFloat(pib.getInvoiceTotalPriceTax()));//价税合计，保留两位小数
                System.out.println("发票金额："+MyDateUtil.getTxFloat(pib.getInvoiceTotalPriceTax()));
                request.setSignatureParameter(sCodeNameService.getBWListByType(FinalUtil.CODE_NAME_INVOICE_SIGNATURE_BW).get(0).getCode());//百望所需要的默认签名值
                if (pib.getDeviceType() == "1") {
                    //设备类型为1税控盘，必填
                    request.setTaxDiskNo(pib.getTaxDiskNo());//税控盘编号
                    request.setTaxDiskKey(pib.getTaxDiskKey());//税控盘口令
                    request.setTaxDiskPassword(pib.getTaxDiskPassword());//税务数字证书密码
                }
                request.setGoodsCodeVersion(pib.getGoodsCodeVersion());//商品编码版本号
                request.setConsolidatedTaxRate(pib.getConsolidatedTaxRate());//综合税率
                request.setNotificationNo(pib.getNotificationNo());//通知单编号
                request.setRemarks(pib.getHouseName() == null ? "" : pib.getHouseName() + "/" + pib.getRemarks() == null ? "" : pib.getRemarks());//备注
                //根据InvoiceGuid查询开票明细数据
                // TODO 这个需要重新写一个sql,把getByInvoiceGuid里面的判断条件加上（使用or）parentguid
                List<PInvoiceBlueDetail> pinvoiceDetailNewList = pInvoiceBlueDetailService.getByInvoiceGuid(pib.getGuid());
                List<InvoiceDetails> invoiceDetailsList = new ArrayList<InvoiceDetails>();
                for (PInvoiceBlueDetail pInvoiceDetail : pinvoiceDetailNewList) {
                    //百望开票明细
                    InvoiceDetails invoiceDetails = new InvoiceDetails();
                    invoiceDetails.setGoodsLineNo(pInvoiceDetail.getGoodsLineNo());//明细行号
                    invoiceDetails.setGoodsLineNature(pInvoiceDetail.getGoodsLineNature());//发票行性质，0：正常行 1：折扣行 2：被折扣行
                    invoiceDetails.setGoodsCode(pInvoiceDetail.getGoodsCode());//国税商品编码
                    invoiceDetails.setGoodsExtendCode(pInvoiceDetail.getGoodsExtendCode());//自定义商品编码
                    invoiceDetails.setGoodsName(pInvoiceDetail.getGoodsName());//自定义商品名称
                    invoiceDetails.setGoodsTaxItem(pInvoiceDetail.getGoodsTaxItem());//国税商品名称
                    invoiceDetails.setGoodsSpecification(pInvoiceDetail.getGoodsSpecification());
                    invoiceDetails.setGoodsUnit(pInvoiceDetail.getGoodsUnit());
                    invoiceDetails.setGoodsQuantity(pInvoiceDetail.getGoodsQuantity());
                    if (!("".equals(pInvoiceDetail.getGoodsPrice()) || pInvoiceDetail.getGoodsPrice() == null)) {
                        invoiceDetails.setGoodsPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsPrice()));
                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalPrice()) || pInvoiceDetail.getGoodsTotalPrice() == null)) {
                        invoiceDetails.setGoodsTotalPrice(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalPrice()));//金额,单位：元，保留两位小数

                    }
                    if (!("".equals(pInvoiceDetail.getGoodsTotalTax()) || pInvoiceDetail.getGoodsTotalTax() == null)) {
                        invoiceDetails.setGoodsTotalTax(MyDateUtil.getTxFloat(pInvoiceDetail.getGoodsTotalTax()));//税额
                    }
                    invoiceDetails.setGoodsTaxRate(pInvoiceDetail.getGoodsTaxRate());//税率
                    invoiceDetails.setGoodsDiscountLineNo(pInvoiceDetail.getGoodsDiscountLineNo());
                    invoiceDetails.setPriceTaxMark(pInvoiceDetail.getPriceTaxMark());//含税标志0：不含税 1：含税
                    invoiceDetails.setVatSpecialManagement(pInvoiceDetail.getVatSpecialManagement());
                    invoiceDetails.setFreeTaxMark(pInvoiceDetail.getFreeTaxMark());
                    invoiceDetails.setPreferentialMark(pInvoiceDetail.getPreferential());//是否使用优惠政策 0:未使用，1:使用
                    invoiceDetailsList.add(invoiceDetails);
                }
                request.setInvoiceDetailsList(invoiceDetailsList);
                InvoiceOpenResponse response;
                try {
                    //调用开票
                    response = publicOpenInvoice(request);
                    invoiceOpenSingleResponseMap.put(pinvoiceDetailNewList, response);//<明细list，返回结果>
                    //开票成功后保存蓝票数据
                    if (response != null) {
                        //开票成功后，更新开票记录和明细上带上发票号
                        pib.setInvoiceCode(response.getInvoiceCode());//发票代码
                        pib.setInvoiceNo(response.getInvoiceNo());//发票号码
                        pib.setInvoiceEndTime(String.valueOf(MyDateUtil.StrToDate(response.getInvoiceDate(), MyDateUtil.format_yyyyMMddHHmmss).getTime()));//开票完成日期
                        pib.setTaxControlCode(response.getTaxControlCode());//税控码 对应发票的密码区
                        pib.setInvoiceCheckCode(response.getInvoiceCheckCode());//校验码
                        pib.setInvoiceQrcode(response.getInvoiceQrCode());//二维码
                        pib.setInvoiceStatus(FinalUtil.INVOICE_SUCCESS);//更新发票状态;开票成功
                        pInvoiceBlueService.update(pib); //更新发票记录
                        System.out.println("blue"+pib.getGuid()+"发票号："+pib.getInvoiceNo());
                        for (PInvoiceBlueDetail pb : pinvoiceDetailNewList){
                            pb.setInvoiceNo(response.getInvoiceNo());
                            //TODO 这里可以优化，就是自己写一个updateList的sql。这样把压力放到数据库，减少连接数
                            System.out.println("Detail"+pb.getGuid()+"发票号："+pb.getInvoiceNo());
                            pInvoiceBlueDetailService.update(pb);
                        }
                    }
                } catch (Exception e) {
                    throw new MyException(e.getMessage());
                }
            }

            /**
             *  把开出的所有发票号，取到源发票，然后把所有的发票的税额汇总，然后统一返回给请求方
             */
            JSONObject jsonObject = new JSONObject();
            JSONArray jsonArray = new JSONArray();
            //存放发票号汇总
            StringBuffer invoiceNos = new StringBuffer();
            float invoiceTotalPriceSum = 0.00f; // 总价税合计
            float InvoiceTotalTaxSum = 0.00f; //总税额
            float invoiceTotalPriceTaxSum = 0.00f; //总不含税金额
            for (Map.Entry entry : invoiceOpenSingleResponseMap.entrySet()) {
                //获取开票返回记录
                InvoiceOpenResponse response = (InvoiceOpenResponse)entry.getValue();
                //拼凑参数进行调用发票查询接口
                PInvoiceBlue pInvoiceBlue = new PInvoiceBlue();
                pInvoiceBlue.setGuid(pInvoiceBlueService.getGuidByInvoiceNo(response.getInvoiceNo()));
                pInvoiceBlue.setInvoiceCode(response.getInvoiceCode());
                pInvoiceBlue.setInvoiceNo(response.getInvoiceNo());
                // TODO 开始日期和截止日期都填了一样的，目前没影响，如果后期发现需要按日期查发票，那么这里就需要改
                pInvoiceBlue.setInvoiceStartTime(response.getInvoiceDate());
                pInvoiceBlue.setInvoiceEndTime(response.getInvoiceDate());
                pInvoiceBlue.setSellerTaxNo(sellerTaxNo);         //销方税号
                DecimalFormat fnum = new DecimalFormat("##0.00");
                InvoiceQueryResponse responseRet = callOut_invoiceQueryOutController.invoiceBlueQueryOut(pInvoiceBlue);
                for (InvoiceMainInfo invoiceMainInfo : responseRet.getInvoiceList()) {
                    jsonObject.put("receiptGuid", invoiceBlueParentGuid);//记录guid（单位：分）
                    jsonObject.put("blueRedType", invoiceMainInfo.getInvoiceType());
                    jsonObject.put("invoiceTypeCode", requestJson.getInvoiceTypeCode());
                    jsonObject.put("invoiceCode", invoiceMainInfo.getInvoiceCode());//发票代码（单位：分）
                    // jsonObject.put("invoiceNo", invoiceMainInfo.getInvoiceNo());//发票号码
                    invoiceNos.append(invoiceMainInfo.getInvoiceNo()+",");
                    jsonObject.put("invoiceDate", invoiceMainInfo.getInvoiceDate());//开票日期
                    jsonObject.put("taxControlCode", invoiceMainInfo.getTaxationMode());//税控码
                    jsonObject.put("invoiceCheckCode", invoiceMainInfo.getInvoiceCheckCode());//校验码
                    jsonObject.put("invoiceQrCode", invoiceMainInfo.getInvoiceQrCode());//二维码
                    jsonObject.put("deductibleAmount", invoiceMainInfo.getDeductibleAmount());//扣除额
                    jsonObject.put("invoiceTotalPrice", invoiceMainInfo.getInvoiceTotalPrice());//合计金额
                    jsonObject.put("invoiceTotalTax", invoiceMainInfo.getInvoiceTotalTax());//合计税额
                    jsonObject.put("invoiceTotalPriceTax", invoiceMainInfo.getInvoiceTotalPriceTax());//价税合计
                    //如果是电子发票，则需要调用版式生成的接口
                    if (FinalUtil.INVOICE_TYPE_CODE_026.equals(invoiceMainInfo.getInvoiceTypeCode())) {
                        pInvoiceBlue.setSellerTaxNo(invoiceMainInfo.getSellerTaxNo());//纳税人识别号
                        pInvoiceBlue.setSerialNo(invoiceMainInfo.getSerialNo());//开票流水号
                        pInvoiceBlue.setBuyerPhone(invoiceMainInfo.getBuyerPhone());// 购方客户电话
                        pInvoiceBlue.setBuyerEmail(invoiceMainInfo.getBuyerEmail());// 购方客户邮箱
                        jsonObject.put("invoiceUrl", callOut_invPdfOutController.invPdfOut(pInvoiceBlue));//版式生成电子url
                    }
                    for (PInvoiceBlueDetail invoiceDetails : (List<PInvoiceBlueDetail>) entry.getKey()) {
                        JSONObject json = new JSONObject();
                        //价税合计
                        float totalPriceTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice());
                        //税额 = 价税合计 * 税率 / （1+税率）
                        float totalTax = Float.valueOf(invoiceDetails.getGoodsTotalPrice()) * Float.valueOf(invoiceDetails.getGoodsTaxRate()) / (1 + Float.valueOf(invoiceDetails.getGoodsTaxRate()));
                        //金额（不含税）
                        float totalPrice = (totalPriceTax*100 - totalTax*100)/100;
                        //TODO [注意] invoiceGuid作用很大，后面作废，红冲叫他们都使用这个guid
                        json.put("invoiceGuid",invoiceDetails.getInvoiceGuid()); //把支付平台拆分生成的guid回调回去。
                        json.put("exsysDetailID", invoiceDetails.getExsysDetailId());//外部系统明细idjson.put("goodsLineNo", InvoiceDetailsList.getGoodsLineNo());//明细行号
                        json.put("goodsLineNo", invoiceDetails.getGoodsLineNo());//明细行号
                        json.put("invoiceNo", invoiceDetails.getInvoiceNo());//拆分后明细对应发票号
                        json.put("newInvoiceGuid", invoiceDetails.getNewInvoiceGuid());//拼凑的newguid
                        //TODO 这里存在小数点精确不够的风险，目前没暴露出来。后期如果发现，可能就是这里的问题
                        json.put("goodsTotalPriceTax", fnum.format(totalPriceTax / 100));//价税金额（单位：分），开票时传入的金额
                        json.put("goodsTotalPrice", fnum.format(totalPrice / 100));//税金额（不含税）=价税金额 - 税额（单位：元）
                        json.put("goodsTotalTax", fnum.format(totalTax / 100));//税额（单位：元）
                        json.put("goodsTaxRate", invoiceDetails.getGoodsTaxRate());//税率
                        jsonArray.add(json);
                        invoiceTotalPriceSum = invoiceTotalPriceSum + totalPriceTax;
                        InvoiceTotalTaxSum = InvoiceTotalTaxSum + totalTax;
                        invoiceTotalPriceTaxSum = invoiceTotalPriceTaxSum + totalPrice;
                    }
                    if(invoiceNos.length() > 0){ //截取最后一个逗号
                        jsonObject.put("invoiceNo", invoiceNos.substring(0,invoiceNos.length()-1));//发票号汇总
                    }
                    jsonObject.put("invoiceTotalPrice", fnum.format(invoiceTotalPriceSum / 100));
                    jsonObject.put("invoiceTotalTax", fnum.format(InvoiceTotalTaxSum / 100));
                    jsonObject.put("invoiceTotalPriceTax", fnum.format(invoiceTotalPriceTaxSum / 100));
                    jsonObject.put("invoiceDetailsList", jsonArray);
                }

            }

            //开票成功后，根据外部明细id，在对应的明细上带上发票号
            System.out.println("返回结果"+jsonObject.toString());
            RetJsonObject = jsonObject;
        }
      return RetJsonObject;
    }

    /**
     * 用于根据map的value排序
     * @param map
     * @param <K>
     * @param <V>
     * @return
     */
    public  <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        Map<K, V> result = new LinkedHashMap<>();
        Stream<Map.Entry<K, V>> st = map.entrySet().stream();

        st.sorted(Comparator.comparing(e -> e.getValue())).forEach(e -> result.put(e.getKey(), e.getValue()));
        System.out.println(result);
        exsysIdToPriceMap = (Map<String, Long>) result;
        return result;
    }

    /**
     * 用于根据上限额度分组
     * @param limitation
     * @return
     */
    public long groupByK(long limitation) {
        /**
         * 算法
         * 目的：实现最优分组，使得每次可以获取最接近于指定某个数的值
         * 算法步骤：1，把需要待分组的数进行顺序排序
         *           2，然后从后往前找，找出小于指定K的数M
         *           3，在待组合的数列里面去除M
         *           4，使用K - M 获取新指定值 K
         *           4，重复2，3,4
         *           5,当序列中最小值N > K 时，结束查找。
         */
        Map<String,Long> newMap =  new LinkedHashMap<>();
        Map<String,Long> newBeyondMap;
        long sum = 0;//总金额
        long limits = limitation;
        ListIterator<Map.Entry<String,Long>> i = new ArrayList<>(exsysIdToPriceMap.entrySet()).listIterator(exsysIdToPriceMap.size());
        //倒序遍历map
        while(i.hasPrevious()) {
            Map.Entry<String, Long> entry=i.previous();
            if (entry.getValue() >= limits){
                newBeyondMap =  new LinkedHashMap<>();
                newBeyondMap.put(entry.getKey(),entry.getValue());
                i.remove();
                exsysIdToPriceMap.remove(entry.getKey());
                groupbeyondList.add(newBeyondMap);
                continue;
            }
            if (entry.getValue() < limitation){
                sum+=entry.getValue();
                limitation = limitation - sum;
                //把对应的元素放到指定的map中
                newMap.put(entry.getKey(),entry.getValue());
                //移除源对应的元素
                i.remove();
                exsysIdToPriceMap.remove(entry.getKey());
            }
        }
        groupList.add(newMap);
        System.out.println(groupList);
        System.out.println(groupbeyondList);
        return limitation;
    }
    /**
     * 算法：
     * 目的：根据上限金额少一块为标准开票金额，对开票的价税合计金额进行拆分合并
     * 步骤：1，
     * @param groupbeyondList
     * @param standardPrice
     */
    public Map<String,Long> groupSingleByLimition(List<Map<String,Long>> groupbeyondList,long standardPrice){
        Map<String,Long> strMaps = new LinkedHashMap<>(); //存放待拆的1 Map《外部明细id,金额》
        Map<String,Long> targerGroupMap = new LinkedHashMap<>(); //存放拆分完成的Map《外部明细id,金额》
        // Map<String,Long> exId2additionPriceMap = new LinkedHashMap<>(); //存放单张拆完剩余的Map《外部明细id,金额》
        String targer = null;
        long additionPrice = 0; //没张超额剩余的发票金额
        for (Map<String,Long> strMap : groupbeyondList){
            for (Map.Entry entry : strMap.entrySet()) {
                strMaps.put((String)entry.getKey(),(Long)entry.getValue());
            }
        }
        for (Map.Entry entry : strMaps.entrySet()) {
            long totalPriceTax = (Long) entry.getValue();
            //为了防止一张发票明细拆多张会把targerGroupMap里面的值覆盖掉,所以在后面拼接一个【-当前的时间戳】，便于使用的时候根据 - 去截取
            while (totalPriceTax >= standardPrice){
                targerGroupMap.put((String)entry.getKey()+"+"+new Date().getTime(),standardPrice);
                System.out.println((String)entry.getKey()+"+"+new Date().getTime()+" *** **** "+standardPrice);
                //结束条件
                totalPriceTax = totalPriceTax - standardPrice;
            }
            if (totalPriceTax > 0){
                exId2additionPriceMap.put((String)entry.getKey(),totalPriceTax);
            }
        }
        int i = 0 ;
        for ( Map.Entry entry : exId2additionPriceMap.entrySet()){
            i++;
            //把剩余的参数添加，然后把参数放到targerGroupMap
            additionPrice =  additionPrice  + (Long)entry.getValue();
            //保存giud
            guidlists.append(entry.getKey()).append(",");
            if (additionPrice >= standardPrice){
                String ss = guidlists.toString();
                additionPrice = additionPrice - (Long)entry.getValue();
                targerGroupMap.put(ss.split(","+entry.getKey())[0]+"+"+new Date().getTime(),additionPrice);
                guidlists.setLength(0);//清空内容
                //把additionPrice的值重新赋值
                additionPrice = (Long)entry.getValue();
            }else{
                if(additionPrice < standardPrice && i == exId2additionPriceMap.size()){

                    targerGroupMap.put(guidlists.substring(0,guidlists.length()-1)+"+"+new Date().getTime(),additionPrice);


                }
            }
        }
        return targerGroupMap;

    }

    /**
     * 开票公共类
     * @param request
     * @return
     */
    public InvoiceOpenResponse publicOpenInvoice(InvoiceOpenRequest request) {
        String token = invOpenOutService.getToken();
        IBopClient client = invOpenOutService.getClient();
        InvoiceOpenResponse response = client.execute(request, token, InvoiceOpenResponse.class);
        return response;
    }

}
